#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <sqlite3.h>
#import <UIKit/UIApplication.h>
#import <SpringBoard/SBApplicationController.h>
#import <SpringBoard/SBApplication.h>
#import "../LockInfo/Plugin.h"

#import "PIPlugin.h"
#import "PIIntegration.h"


static BOOL isYesterday(NSDate* date)
{
	return isSameDay(date, [NSDate dateWithTimeIntervalSinceNow:-SECONDS_PER_DAY]);
}

const NSString* PIiPhoneIdentifier = @"pocketinformant";
const NSString* PIiPadIdentifier = @"pocketinformantiPad";


@implementation PIIntegration

+(NSString*) base64EncodeString:(NSString*)src {
	static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	
	NSMutableString *dest = [[NSMutableString alloc] initWithString:@""];
	int length = [src length];
	
	for (int i = 0; i < length; i += 3) {
		for (int nib = 0; nib < 4; nib++) {
			// nibble:nib from char:byt
			int byt = (nib == 0) ? 0 : nib-1;
			int ix = (nib+1) * 2;
			
			if (i+byt >= length)
				break;
			
			// extract the top bits of the nibble, if valid
			unsigned char curr = (([src characterAtIndex:i+byt] << (8-ix)) & 0x3F);
			
			// extract the bottom bits of the nibble, if valid
			if (i+nib < length)
				curr |= (([src characterAtIndex:i+nib] >> ix) & 0x3F);
			
			[dest appendFormat:@"%c", base64[curr]];
		}
	}
	
	if ((length % 3) == 1)
		[dest appendString:@"=="];
	else if ((length % 3) == 2)
		[dest appendString:@"="];
	
	return dest;
}

+(SBApplication*) getApplication {
	Class cls = objc_getClass("SBApplicationController");
	SBApplicationController* ctr = [cls sharedInstance];
	
	SBApplication* app = [ctr applicationWithDisplayIdentifier:PIiPhoneIdentifier];
	if (app == nil)
		app = [ctr applicationWithDisplayIdentifier:PIiPadIdentifier];
	
	NSLog(@"LI:PITodo: Discovered application path: %@", app);
	
	return app;
}

+(NSString*) getDatabasePath:(NSString*) path {
	NSString* thePath = [path stringByAppendingString:@"/Documents/pidata.sqlite"];
	
	return thePath;
}


#pragma mark Date/time conversions for PI representations

+(NSDate*) localDateForDate:(double)date {
	// PI stores dates as relative to UTC
	NSDate* utcDate = [NSDate dateWithTimeIntervalSinceReferenceDate:date];
	
	// Get seconds from UTC for the required date
	NSInteger localOffset = [[NSTimeZone systemTimeZone] secondsFromGMTForDate:utcDate];
	
	return [NSDate dateWithTimeIntervalSinceReferenceDate:(date - localOffset)];
}

+(NSDate*) localDateForTime:(double)date {
	// PI stores times as local to the current TZ
	return [NSDate dateWithTimeIntervalSinceReferenceDate:date];
}

+(NSString*) formatDate:(NSBundle*)bundle date:(NSNumber*)dateNum time:(BOOL)time format:(DateFormatterFormat)format {
	NSString* result = @"\u2205"; // Empty set symbol
	
	if (dateNum.doubleValue != 0.0) {
		// Required time in UTC
		NSDate* localDate = time ? [PIIntegration localDateForTime:dateNum.doubleValue]
		                         : [PIIntegration localDateForDate:dateNum.doubleValue]; 
		
		NSDateFormatter* df = [[NSDateFormatter alloc] init];
		if (format == DateFormatterLongFormat) {
			// Use long date format - eg "November 23, 1937"
			df.dateStyle = NSDateFormatterLongStyle;
			df.timeStyle = NSDateFormatterNoStyle;
		}
		else if (format == DateFormatterCompressedFormat) {
			// Use shortest date format - eg "11/23/2010"
			df.dateStyle = NSDateFormatterShortStyle;
			df.timeStyle = NSDateFormatterNoStyle;

			// Change 4 digit year to 2 to make shorted - eg "11/23/10"
			df.dateFormat = [df.dateFormat stringByReplacingOccurrencesOfString:@"yyyy" withString:@"yy"];
		}
		else
			// Use standard date format - eg "Mon 23 Jun"
			df.dateFormat = (NSString*)UIDateFormatStringForFormatType(CFSTR("UIAbbreviatedWeekdayMonthDayFormat"));

		if (format == DateFormatterCompressedFormat) {
			result = [df stringFromDate:localDate];
		}
		else {
			// Use relative dates if suitable
			if (isToday(localDate))
				result = localize(bundle, @"Date/Today");
			else if (isTomorrow(localDate))
				result = localize(bundle, @"Date/Tomorrow");
			else if (isYesterday(localDate))
				result = localize(bundle, @"Date/Yesterday");
			else {
				result = [df stringFromDate:localDate];
				
				// If using long format then append the day name, eg "Mon, November 23, 1937"
				if (format == DateFormatterLongFormat) {
					NSString* replace = localize(bundle, @"Date/Long Format");
					df.dateFormat = @"ccc";
					replace = [replace stringByReplacingOccurrencesOfString:@"%DAY%" withString:[df stringFromDate:localDate]];
					result = [replace stringByReplacingOccurrencesOfString:@"%DATE%" withString:result];
				}
			}
		}

		// Add the time if provided
		if (time) {
			NSDateFormatter* tf = [[NSDateFormatter alloc] init];
			tf.dateStyle = NSDateFormatterNoStyle;
			tf.timeStyle = NSDateFormatterShortStyle;
			
			result = [NSString stringWithFormat:@"%@ %@", result, [tf stringFromDate:localDate]];
			
			[tf release];
		}
		
		DebugLog(@"LI:PITodo: formatting time num=%F Local=%@ Time=%@", dateNum.doubleValue, localDate, result);
		
		[df release];
	}
	
	return result;
}

+(NSString*) nameForAction:(int)action {
	switch (action) {
		case PIActionNext:
			return @"Action/Next Action";
		case PIActionActive:
			return @"Action/Active";
		case PIActionPlanning:
			return @"Action/Planning";
		case PIActionDelegated:
			return @"Action/Delegated";
		case PIActionWaiting:
			return @"Action/Waiting";
		case PIActionHold:
			return @"Action/Hold";
		case PIActionPostponed:
			return @"Action/Postponed";
		case PIActionSomeday:
			return @"Action/Someday";
		case PIActionCancelled:
			return @"Action/Cancelled";
		case PIActionReference:
			return @"Action/Reference";
		default:
			return @"Action/None";
	}
}

+(NSString*) nameForImportance:(int)importance {
	switch (importance) {
		case PIImportanceTop:
			return @"Importance/Top";
		case PIImportanceHigh:
			return @"Importance/High";
		case PIImportanceNormal:
			return @"Importance/Normal";
		case PIImportanceLow:
			return @"Importance/Low";
		default:
			return @"Importance/None";
	}
}

+(NSString*) iconFileForAction:(int)action
{
	NSString* actionIcon = nil;
	
	switch (action) {
		case PIActionNext:
			actionIcon = @"NextAction";
			break;
		case PIActionActive:
			actionIcon = @"Active";
			break;
		case PIActionPlanning:
			actionIcon = @"Planning";
			break;
		case PIActionDelegated:
			actionIcon = @"Delegated";
			break;
		case PIActionWaiting:
			actionIcon = @"Waiting";
			break;
		case PIActionHold:
			actionIcon = @"Hold";
			break;
		case PIActionPostponed:
			actionIcon = @"Postponed";
			break;
		case PIActionSomeday:
			actionIcon = @"Someday";
			break;
		case PIActionCancelled:
			actionIcon = @"Cancelled";
			break;
		case PIActionReference:
			actionIcon = @"Reference";
			break;
		default:
			actionIcon = nil;
			break;
	}
	
	return actionIcon;
}

+(UIColor*) topImportanceBGColorForPrefs:(NSDictionary*)prefs
{
	// Top importance color
	NSArray* piTop = [prefs valueForKey:@"PICalPriorityColor_1_background"];
	if (piTop != nil)
		return [UIColor colorWithRed:[[piTop objectAtIndex:0] doubleValue] 
							   green:[[piTop objectAtIndex:1] doubleValue]
								blue:[[piTop objectAtIndex:2] doubleValue]
							   alpha:[[piTop objectAtIndex:3] doubleValue]];
	else
		return [UIColor colorWithRed:1 
							   green:0
								blue:0
							   alpha:1];
}

+(UIColor*) highImportanceBGColorForPrefs:(NSDictionary*)prefs
{
	// High importance color	
	NSArray* piHigh = [prefs valueForKey:@"PICalPriorityColor_5_background"];
	if (piHigh != nil)
		return [UIColor colorWithRed:[[piHigh objectAtIndex:0] doubleValue] 
							   green:[[piHigh objectAtIndex:1] doubleValue]
								blue:[[piHigh objectAtIndex:2] doubleValue]
							   alpha:[[piHigh objectAtIndex:3] doubleValue]];
	else 
		return [UIColor colorWithRed:1 
							   green:0.4980392156862745
								blue:0
							   alpha:1];
}

+(UIColor*) normalImportanceBGColorForPrefs:(NSDictionary*)prefs
{
	// Normal importance color
	NSArray* piNormal = [prefs valueForKey:@"PICalPriorityColor_10_background"];
	if (piNormal != nil)
		return [UIColor colorWithRed:[[piNormal objectAtIndex:0] doubleValue] 
							   green:[[piNormal objectAtIndex:1] doubleValue]
								blue:[[piNormal objectAtIndex:2] doubleValue]
							   alpha:[[piNormal objectAtIndex:3] doubleValue]];
	else 
		return [UIColor colorWithRed:0
							   green:1
								blue:0
							   alpha:1];
}

+(UIColor*) lowImportanceBGColorForPrefs:(NSDictionary*)prefs
{
	// Low importance color
	NSArray* piLow = [prefs valueForKey:@"PICalPriorityColor_15_background"];
	if (piLow != nil)
		return [UIColor colorWithRed:[[piLow objectAtIndex:0] doubleValue] 
							   green:[[piLow objectAtIndex:1] doubleValue]
								blue:[[piLow objectAtIndex:2] doubleValue]
							   alpha:[[piLow objectAtIndex:3] doubleValue]];
	else 
		return [UIColor colorWithRed:0
							   green:1
								blue:1
							   alpha:1];
}

+(UIColor*) overdueBGColorForPrefs:(NSDictionary*)prefs
{
	// Overdue color
	NSArray* piOverdue = [prefs valueForKey:@"PIColorTaskOverdue"];
	if (piOverdue != nil)
		return [UIColor colorWithRed:[[piOverdue objectAtIndex:0] doubleValue] 
							   green:[[piOverdue objectAtIndex:1] doubleValue]
								blue:[[piOverdue objectAtIndex:2] doubleValue]
							   alpha:[[piOverdue objectAtIndex:3] doubleValue]];
	else
		return [UIColor colorWithRed:1
							   green:0.4470588235294118
								blue:0.4470588235294118
							   alpha:1];	
}

@end
